﻿namespace ScrumLite.WebUx.Controllers
{
    using System;
    using System.Collections;
    using System.Globalization;
    using System.Net;
    using System.Security.Permissions;
    using System.Web;
    using System.Web.Mvc;
    using ScrumLite.Services;
    using ScrumLite.WebUx.Models;
    using ScrumLite.Gateways;
    using ScrumLite.Services.Contracts;
    using System.Text.RegularExpressions;
    using System.Web.Routing;

    public class ContextBaseController : Controller
    {
        private const string DisplayNameCacheKey = "__DisplayName";
        private const string UserRolesKey = "__UserRoles";

        protected IProfileService Profile { get; set; }
        protected IProjectsService Projects { get; set; }

        public ContextBaseController()
            : this(new ProfileServiceClient())
        {
        }

        public ContextBaseController(IProfileService profile)
            : this(profile, new ProjectsServiceClient())
        {
        }

        public ContextBaseController(IProfileService profile, IProjectsService projects)
        {
            this.Profile = profile;
            this.Projects = projects;
        }

        protected override void HandleUnknownAction(string actionName)
        {
            try
            {
                base.HandleUnknownAction(actionName);
            }
            catch (InvalidOperationException exception)
            {
                ErrorViewData data = new ErrorViewData()
                {
                    Exception = exception
                };
                if (HttpContext.IsDebuggingEnabled)
                {
                    RenderView("ErrorDebug", data);
                }
                else
                {
                    RenderView("Error", data);
                }
            }
        }

        protected override void InvokeActionMethod(System.Reflection.MethodInfo methodInfo, RouteValueDictionary values)
        {
            object projectName;
            if (this.RouteData.Values.TryGetValue("projectName", out projectName))
            {
                Guid projectId;
                Project project;
                string convertedProjectIdentifier = projectName as string;
                if (!GuidTryParse(convertedProjectIdentifier, out projectId))
                {
                    project = Projects.GetProjectByName(convertedProjectIdentifier);
                    projectId = project.Id;
                }
                else
                {
                    project = Projects.GetProject(projectId.ToString());
                }
                this.RouteData.Values.Add("projectId", projectId.ToString());
            }
            base.InvokeActionMethod(methodInfo, values);
        }

        [SecurityPermission(SecurityAction.LinkDemand, Unrestricted = true)]
        protected override void OnActionExecuting(FilterExecutingContext
        filterContext)
        {
            string userName = HttpContext.User.Identity.Name;

            object projectId;

            if (filterContext.RouteData.Values.TryGetValue("projectId", out projectId))
            {
                Project project;
                project = Projects.GetProject(projectId.ToString());

                string group = project.Group == null ? null : project.Group.ToUpperInvariant();

                if (group == null || !Profile.IsUserInRole(userName, group))
                    throw new HttpException((int)HttpStatusCode.Unauthorized, String.Format(CultureInfo.InvariantCulture, "You don't have permissions to access project: {0}", project.Name));
            }

            base.OnActionExecuting(filterContext);
        }

        /// <summary>
        /// This is used to decode the "\" character in
        /// DeliverablesController
        /// RequestController
        /// WorkAreasController
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        protected virtual string Decoder(string value)
        {
            string result = value;
            if (!String.IsNullOrEmpty(value))
            {
                result = value.Replace("¿5C¿", "%5C");
                result = value.Replace("¿punto¿", ".");
            }
            return Uri.UnescapeDataString(result);
        }

        [SecurityPermission(SecurityAction.LinkDemand, Unrestricted=true)]
        protected override void RenderView(string viewName, string masterName, object viewData)
        {
            ContextViewData contextData;
            IDictionary castedViewData = viewData as IDictionary;

            if (viewData == null || (castedViewData != null && castedViewData.Count == 0))
            {
                contextData = new ContextViewData();
            }
            else
            {
                contextData = viewData as ContextViewData;
            }

            if (contextData == null)
            {
                throw new InvalidOperationException(Properties.Resources.ContextBaseControllerWrongViewData);
            }

            contextData.UserName = HttpContext.User.Identity.Name;

            if (HttpContext.Session[DisplayNameCacheKey] == null)
            {
                HttpContext.Session[DisplayNameCacheKey] = Profile.GetFullName(contextData.UserName);
            }

            contextData.DisplayName = HttpContext.Session[DisplayNameCacheKey] as string;

            base.RenderView(viewName, masterName, contextData);
        }

        protected virtual void RedirectToAction(object viewToRedirect)
        {
            base.RedirectToAction(new RouteValueDictionary(viewToRedirect));
        }

        private static bool GuidTryParse(string valueToParse, out Guid parsedGuid)
        {
            if (valueToParse == null)
                throw new ArgumentNullException("valueToParse");
            Regex format = new Regex(
                "^[A-Fa-f0-9]{32}$|" +
                "^({|\\()?[A-Fa-f0-9]{8}-([A-Fa-f0-9]{4}-){3}[A-Fa-f0-9]{12}(}|\\))?$|" +
                "^({)?[0xA-Fa-f0-9]{3,10}(, {0,1}[0xA-Fa-f0-9]{3,6}){2}, {0,1}({)([0xA-Fa-f0-9]{3,4}, {0,1}){7}[0xA-Fa-f0-9]{3,4}(}})$");
            Match match = format.Match(valueToParse);
            if (match.Success)
            {
                parsedGuid = new Guid(valueToParse);
                return true;
            }
            else
            {
                parsedGuid = Guid.Empty;
                return false;
            }
        }
    }
}
